package com.chase.passwordmaster.data

import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.chase.passwordmaster.entity.Account
import com.chase.passwordmaster.entity.DecryptedAccount
import org.litepal.LitePal
import org.litepal.crud.DataSupport


/**
 * 这一层是知道DecryptedAccount这个东西的，最好是让View层不知道还有Account和DecryptedAccount之分
 */
class AccountViewModel : ViewModel() {
    companion object {
        val accountLiveData: MutableLiveData<ArrayList<DecryptedAccount>> by lazy {
            MutableLiveData<ArrayList<DecryptedAccount>>().also { itAccountLiveData ->
                val accountListFromDB = LitePal.findAll(Account::class.java)
                accountListFromDB?.let { itAccountListFromDB ->
                    itAccountLiveData.value =
                        itAccountListFromDB.map { DecryptedAccount(it) } as ArrayList<DecryptedAccount>
                }
            }
        }
    }

    /**
     * @return MutableLiveData<ArrayList<DecryptedAccount>> 已加密账号列表的LiveData
     */
    fun getAccountLiveData() = accountLiveData

//    /**
//     * Get account by id
//     */
//    fun getAccountById(id: Int): Account? {
//        return accountLiveData.value?.find {
//            it.id == id
//        }
//    }

    /**
     * 按照在accountLiveData中的index查找EncryptedAccount
     * Get EncryptedAccount by index in accountLiveData
     *
     * @param index
     * @return DecryptedAccount? 已加密账号（未查找到则返回空引用）
     */
    fun getAccount(index: Int): DecryptedAccount? = accountLiveData.value?.get(index)

    fun addAccount(decryptedAccount: DecryptedAccount): Boolean {
        accountLiveData.value?.run {
            add(decryptedAccount)
            accountLiveData.value = this
        }

        // Insert to data base
        return decryptedAccount.save()
    }

    /**
     * 删除在accountLiveData中的index位置的EncryptedAccount
     * Remove EncryptedAccount with index in accountLiveData
     *
     * @param index
     */
    fun removeAccount(index: Int) {
        if (index < 0) {
            Log.i("GSC:AccountViewModel", "index in removeAccount() == -1")
            return
        }

        accountLiveData.value?.get(index)?.delete()
        accountLiveData.value?.run {
            removeAt(index)
            accountLiveData.value = this
        }
    }

    /**
     * 删除accountLiveData中所有EncryptedAccount
     * Remove all EncryptedAccount in accountLiveData
     */
    fun removeAll() {
        LitePal.deleteAll(Account::class.java)

        accountLiveData.value?.run {
            clear()
            accountLiveData.value = this
        }
    }

    inner class AccountIndexed(decryptedAccount: DecryptedAccount, val index: Int) :
        DecryptedAccount(
            decryptedAccount.website,
            decryptedAccount.username,
            decryptedAccount.password,
            decryptedAccount.name,
            decryptedAccount.tel,
            decryptedAccount.qq,
            decryptedAccount.weChat,
            decryptedAccount.email,
            decryptedAccount.memo
        )

    /**
     * Find accounts by website
     * WARNING: Do not guarantee that the index of AccountIndexed
     * is still correct after inserting or removing an instance of
     * Account.
     *
     * @param website
     * @return List<DecryptedAccount> list of result
     */
    fun findAccountByWebsite(website: String): List<DecryptedAccount> {
        if (website.isBlank()) {
            return ArrayList()
        }

        val result = ArrayList<AccountIndexed>()
        accountLiveData.value?.forEachIndexed { index, decryptedAccount ->
            // Do not need to get decrypted account
            // because website does not be decrypted
            // But we should also call its function
            if (decryptedAccount.website.contains(website, ignoreCase = true)) {
                result.add(AccountIndexed(decryptedAccount, index))
            }
        }
        return result
    }

    /**
     * Find account by username
     * WARNING: Do not guarantee that the index of AccountIndexed
     * is still correct after inserting or removing instances of
     * Account.
     *
     * @param username
     * @return List<DecryptedAccount> list of result
     */
    fun findAccountByUsername(username: String): List<DecryptedAccount> {
        if (username.isBlank()) {
            return ArrayList()
        }

        val result = ArrayList<AccountIndexed>()
        accountLiveData.value?.forEachIndexed { index, decryptedAccount ->
            // Get decrypted account and then judge it
            if (decryptedAccount.username.contains(username, ignoreCase = true)) {
                result.add(AccountIndexed(decryptedAccount, index))
            }
        }
        return result
    }

    fun findAccountByTel(tel: String): List<DecryptedAccount> {
        if (tel.isBlank()) {
            return ArrayList()
        }

        val result = ArrayList<AccountIndexed>()
        accountLiveData.value?.forEachIndexed { index, decryptedAccount ->
            if (decryptedAccount.tel?.contains(tel, ignoreCase = true) == true) {
                result.add(AccountIndexed(decryptedAccount, index))
            }
        }
        return result
//
//        return accountLiveData.value
//            ?.mapIndexed { index, decryptedAccount -> AccountIndexed(decryptedAccount, index) }
//            ?.filter { it.tel?.contains(tel, ignoreCase = true) ?: false }
//            ?: ArrayList<AccountIndexed>()
    }

    /**
     * Find accounts by memo
     * WARNING: Do not guarantee that the index of AccountIndexed
     * is still correct after inserting or removing instances of
     * Account.
     *
     * @param memo
     * @return List<DecryptedAccount> list of result
     */
    fun findAccountByMemo(memo: String): List<DecryptedAccount> {
        if (memo.isBlank()) {
            return ArrayList()
        }

        val result = ArrayList<AccountIndexed>()
        accountLiveData.value?.forEachIndexed { index, decryptedAccount ->
            if (decryptedAccount.memo?.contains(memo, ignoreCase = true) == true) {
                result.add(AccountIndexed(decryptedAccount, index))
            }
        }
        return result

//        // Maybe it is inefficient, but both of them are O(n)
//        // Functional programming lol!
//        return accountLiveData.value
//            ?.mapIndexed { index, decryptedAccount -> AccountIndexed(decryptedAccount, index) }
//            ?.filter { it.memo?.contains(memo, ignoreCase = true) ?: false }
//            ?: ArrayList<AccountIndexed>()
    }

    fun findAccountByEmail(email: String): List<DecryptedAccount> {
        if (email.isBlank()) {
            return ArrayList()
        }

        val result = ArrayList<AccountIndexed>()
        accountLiveData.value?.forEachIndexed { index, decryptedAccount ->
            if (decryptedAccount.email?.contains(email, ignoreCase = true) == true) {
                result.add(AccountIndexed(decryptedAccount, index))
            }
        }
        return result

//        return accountLiveData.value
//            ?.mapIndexed { index, decryptedAccount -> AccountIndexed(decryptedAccount, index) }
//            ?.filter { it.email?.contains(email, ignoreCase = true) ?: false }
//            ?: ArrayList<AccountIndexed>()
    }

    fun findAccountByQQ(qq: String): List<DecryptedAccount> {
        if (qq.isBlank()) {
            return ArrayList()
        }

        val result = ArrayList<AccountIndexed>()
        accountLiveData.value?.forEachIndexed { index, decryptedAccount ->
            if (decryptedAccount.qq?.contains(qq, ignoreCase = true) == true) {
                result.add(AccountIndexed(decryptedAccount, index))
            }
        }
        return result

//        return accountLiveData.value
//            ?.mapIndexed { index, decryptedAccount -> AccountIndexed(decryptedAccount, index) }
//            ?.filter { it.qq?.contains(qq, ignoreCase = true) ?: false }
//            ?: ArrayList<AccountIndexed>()
    }

    fun findAccountByWeChat(weChat: String): List<DecryptedAccount> {
        if (weChat.isBlank()) {
            return ArrayList()
        }

        val result = ArrayList<AccountIndexed>()
        accountLiveData.value?.forEachIndexed { index, decryptedAccount ->
            if (decryptedAccount.weChat?.contains(weChat, ignoreCase = true) == true) {
                result.add(AccountIndexed(decryptedAccount, index))
            }
        }
        return result

//        return accountLiveData.value
//            ?.mapIndexed { index, decryptedAccount -> AccountIndexed(decryptedAccount, index) }
//            ?.filter { it.weChat?.contains(weChat, ignoreCase = true) ?: false }
//            ?: ArrayList<AccountIndexed>()
    }

    fun findAccountByName(name: String): List<DecryptedAccount> {
        if (name.isBlank()) {
            return ArrayList()
        }

        val result = ArrayList<AccountIndexed>()
        accountLiveData.value?.forEachIndexed { index, decryptedAccount ->
            if (decryptedAccount.name?.contains(name, ignoreCase = true) == true) {
                result.add(AccountIndexed(decryptedAccount, index))
            }
        }
        return result

//        return accountLiveData.value
//            ?.mapIndexed { index, decryptedAccount -> AccountIndexed(decryptedAccount, index) }
//            ?.filter { it.name?.contains(name, ignoreCase = true) ?: false }
//            ?: ArrayList<AccountIndexed>()
    }

//    fun findAccounts(q: String): List<List<DecryptedAccount>>? {
//        if (q.isBlank()) {
//            return null
//        }
//
//        val result = listOf<MutableList<DecryptedAccount>>(
//            ArrayList(),
//            ArrayList(),
//            ArrayList(),
//            ArrayList(),
//            ArrayList(),
//            ArrayList(),
//            ArrayList()
//        )
//
//        accountLiveData.value?.forEachIndexed { index, decryptedAccount ->
//            decryptedAccount.run {
//                if (website.contains(q, ignoreCase = true)) {
//                    result[0].add(AccountIndexed(decryptedAccount, index))
//                }
//                if ()
//            }
//        }
//    }

    /**
     * Group accounts by website
     * WARNING: Do not guarantee that the index of AccountIndexed
     * is still correct after inserting or removing instances of
     * Account.
     *
     * @return Map<String, List<DecryptedAccount>>
     */
    fun groupByWebsite(): Map<String, List<DecryptedAccount>> =
        (accountLiveData.value
            ?.mapIndexed { index, value -> AccountIndexed(value, index) }
            ?: ArrayList())
            .groupBy { it.website }

    /**
     * 获取accountLiveData数据大小，若accountLiveData尚未设置value，返回0
     *
     * @return Int
     */
    fun getAccountSize() = accountLiveData.value?.size ?: 0

    /**
     * 获取accountLiveData中网站数量
     *
     * @return Int
     */
    fun getWebsiteSize() = accountLiveData.value?.distinctBy { it.website }?.size ?: 0

    /**
     * 获取当前数据库中存储的数据数量
     *
     * @return Int
     */
    fun getRecordSizeInDB() = LitePal.count(Account::class.java)
}